﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using XMLGener.Data;

namespace XMLGener.GUI
{
    /// <summary>
    /// This class represents XMLGenerNode for showing this
    /// node into TreeView.
    /// </summary>
    public class XTreeNode : TreeNode
    {
        /// <summary>
        /// Default constructor.
        /// </summary>
        public XTreeNode()
        {
        }

        /// <summary>
        /// This map is used to assign image index to XMLGenerNode type(attribute,element etc).
        /// </summary>
        private static Dictionary<XMLGenerNode.XNodeType, int> imageMap = createDictionary();
        
        /// <summary>
        /// Create map
        /// </summary>
        /// <returns>map of images index</returns>
        private static Dictionary<XMLGenerNode.XNodeType,int> createDictionary()
        {        
            Dictionary<XMLGenerNode.XNodeType,int> result  = new Dictionary<XMLGenerNode.XNodeType,int>();
            result.Add(XMLGenerNode.XNodeType.ELEMENTS, 0);
            result.Add(XMLGenerNode.XNodeType.ATTRIBUTE, 1);
            result.Add(XMLGenerNode.XNodeType.ELEMENT, 2);
            result.Add(XMLGenerNode.XNodeType.TEXTNODE, 3);
            result.Add(XMLGenerNode.XNodeType.NONE, -1);
            result.Add(XMLGenerNode.XNodeType.ELEMENTTYPES, 4);
            result.Add(XMLGenerNode.XNodeType.DATATYPE, 5);
            result.Add(XMLGenerNode.XNodeType.DATATYPES, 5);

            return result;
        }

        /// <summary>
        /// Constructor with default value of showContentType
        /// </summary>
        /// <param name="node"></param>
        public XTreeNode(XMLGenerNode node) : this(node, true) { }

        /// <summary>
        /// Create XTreeNode with data source.
        /// </summary>
        /// <param name="node">data source of this node</param>
        /// <param name="showContentType">indication for showing content type in tree (in braces)</param>
        public XTreeNode(XMLGenerNode node, bool showContentType)
        {
            this.xnode = node;
            if (this.xnode != null)
                this.xnode.originalChanged += sourceNodeChanged;
            this.ImageIndex = imageMap[node.xtype];
            this.SelectedImageIndex = imageMap[node.xtype];
            if (xnode.isCopy)
            {
                this.ForeColor = System.Drawing.Color.Gray;
            }
            else
            {
                this.ForeColor = System.Drawing.Color.Black;
            }
            this.showContentType = showContentType;
            refresh();
        }

        ~XTreeNode()
        {
            if (this.xnode != null)
                this.xnode.originalChanged -= sourceNodeChanged;
        }
        private void sourceNodeChanged()
        {
            //this.Nodes.Clear();
            this.Nodes.Clear();
            refreshTreeStructure(this);
             //this.xnode
        }

        private bool _showContentType =false;
        
        /// <summary>
        /// This property indicate if property Text will contain content type
        /// </summary>
        public bool showContentType{
            get
            {
                return _showContentType;
            }
            set
            {
                if (value!=_showContentType)
                {
                    _showContentType = value;
                    refresh();
                }
            }
        }
        
        /// <summary>
        /// Refresh text of this node.
        /// </summary>
        public void refresh()
        {
            if (showContentType)
            {
                if (this.xnode is XDataType)
                    this.Text = this.xnode.name + " (" + (this.xnode as XDataType).parentTypeName + ")";
                else if (this.xnode is XMLGenerElement)
                { 
                    XMLGenerElement elem = xnode as XMLGenerElement;
                    if (elem.baseType!=null)
                        this.Text = this.xnode.name + " (type: " + this.xnode.parentTypeName + ")";
                    else
                        this.Text = this.xnode.name + " (" + this.xnode.contentType + ")";
                }
                else
                    this.Text = this.xnode.name + " (" + this.xnode.contentType + ")";
            }
            else
            {
                this.Text = this.xnode.name;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="count"></param>
        public XTreeNode movePosition(int count)
        {
            if (this.Parent== null)
                return null;

            if (this.xnode.changePosition(count))
            { 
                int pos = this.Parent.Nodes.IndexOf(this);
                
                // keep reference to parent, because after remove
                // parent of this instance will be lost (null)
                TreeNode parent = this.Parent;
                parent.Nodes.RemoveAt(pos);
                parent.Nodes.Insert(pos + count, this);
                return this;
            }
            return null;
        
        }

        /// <summary>
        /// 
        /// </summary>
        public void delete()
        {
            if (this.Parent!=null)
            {
                this.Parent.Nodes.Remove(this);
            }
            this.xnode.delete(); 
        }


        /// <summary>
        /// Data source of this node.
        /// </summary>
        public XMLGenerNode xnode = null;


        //public List<XTreeNode> dataTypes = null;

        /// <summary>
        /// <para> Function creates mirror tree structure, where source is
        /// tree composed from XMLGenerNode and mirror is composed of XTreeNode.
        /// Created structure is linked to source for manipulating the structure.</para>
        /// <para>This structure is used for showing XMLGenerNode in TreeView component</para>
        /// </summary>
        /// <param name="rootNode">Root node of structure to create</param>
        /// <returns>Root node of created tree structure</returns>
        public static XTreeNode createStructure(XMLGenerNode rootNode)
        {
            if (rootNode == null)
                return null;
            // for creating tree structure is used depth-first traversal
            Stack<XTreeNode> stack = new Stack<XTreeNode>();

            // main recursive idea is to create new XTreeNode,
            // link source data to it and push it into stack 
            XTreeNode result = new XTreeNode(rootNode);
            stack.Push(result); 

            while (stack.Count > 0)
            {
                XTreeNode node = stack.Pop();
                foreach (XMLGenerNode iNode in node.xnode.allChildNodes)
                {
                    XTreeNode newNode = new XTreeNode(iNode);
                    node.Nodes.Add(newNode);
                    stack.Push(newNode);
                }
                
            }
            return result;
        }

        public static void refreshTreeStructure(XTreeNode treeNode)
        {
            if (treeNode == null)
                return ;
            XMLGenerNode xnode = treeNode.xnode;
            treeNode.Nodes.Clear();
            if (xnode == null)
                return;
               
            // for creating tree structure is used depth-first traversal
            Stack<XTreeNode> stack = new Stack<XTreeNode>();

            // main recursive idea is to create new XTreeNode,
            // link source data to it and push it into stack 
            stack.Push(treeNode); 

            while (stack.Count > 0)
            {
                XTreeNode node = stack.Pop();
                foreach (XMLGenerNode iNode in node.xnode.allChildNodes)
                {
                    XTreeNode newNode = new XTreeNode(iNode);
                    node.Nodes.Add(newNode);
                    stack.Push(newNode);
                }
                
            }
            treeNode.refresh();
           
        
        }

        /// <summary>
        /// Function creates tree structure of nodes for direct adding into
        /// tree component. It creates tree structure, where parent node is 
        /// identified from child by parentTypeName property.
        /// </summary>
        /// <param name="nodes"> collection of nodes</param>
        /// <returns>list of tree structures</returns>
        public static List<XTreeNode> createTypeStructure(XDataTypeCollection nodes)
        {
            XTreeNode outNode;
            return createTypeStructure(nodes,null,out outNode);
        }

        /// <summary>
        /// Function creates tree structure and sets selected node found by 
        /// selectNodeName.
        /// </summary>
        /// <param name="nodes">collection of nodes</param>
        /// <param name="selectNodeName">name of node in collection</param>
        /// <param name="selectedNode">returned selected node found by selectNodeName</param>
        /// <returns>list of tree structures</returns>
        public static List<XTreeNode> createTypeStructure(XDataTypeCollection nodes,string selectNodeName,out XTreeNode selectedNode)
        {
            selectedNode = null;
            if (nodes == null)
            {
                return null;
            }
            
            // add all nodes into map
            Dictionary<string, XTreeNode> map = new Dictionary<string, XTreeNode>();
            foreach (XMLGenerNode node in nodes)
            {
                XTreeNode newNode= new XTreeNode(node,false);
                if (node.name!=null)
                    map.Add(node.name, newNode);
            }
            List<XTreeNode> rootTypes = new List<XTreeNode>();
            
            // main recursive idea is to create new XTreeNode,
            // link source data to it and push it into stack 
            foreach (KeyValuePair<string, XTreeNode> mapedNode in map)
            {
                XTreeNode node = mapedNode.Value;
                string key = node.xnode.parentTypeName;
                if (!string.IsNullOrEmpty(key) && mapedNode.Key!= "any"
                    && map.ContainsKey(key) && !node.xnode.isRootType)
                {
                    map[key].Nodes.Add(node);
                }
                else {
                    rootTypes.Add(node);
                }
            }
            if (selectNodeName != null)
            {
                if (map.ContainsKey(selectNodeName))
                    selectedNode = map[selectNodeName];
            }
            return rootTypes;
        }

    }


}
